﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace X.ROI.UserControls
{
    /// <summary>
    /// Interaction logic for UsrImageView.xaml
    /// </summary>
    public partial class UsrImageView : UserControl, INotifyPropertyChanged
    {
        Point _pointBuff;
        bool _isMouseDown;
        byte[] _imageDataBuffer;
        Int32Rect _imageRect;
        double _ctrlWidth;
        double _ctrlHeight;
        int _mouseDownTick;
        public UsrImageView()
        {
            WImage = new WriteableBitmap(512, 512, 96, 96, PixelFormats.Gray8, null);
            CtrlHeight = WImage.PixelHeight;
            CtrlWidth = WImage.PixelWidth;
            InitializeComponent();
        }
        private void ImageCanvas_MouseDown(object sender, MouseButtonEventArgs e)
        {
            //if(DrawingCanvas.Strokes.Count > 0)
            //{
            //    foreach (var item in DrawingCanvas.Strokes)
            //    {
            //        IDrawingObject drawing = item as IDrawingObject;
            //        if(item.HitTest(e.GetPosition(DrawingCanvas),20))
            //        {
            //            drawing.IsSelected = true;
            //        }
            //        else
            //        {
            //            drawing.IsSelected = false;
            //        }
            //    }
            //}
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                _pointBuff = e.GetPosition(RootCanvas);
                _isMouseDown = true;

                if (CanDoubleClickToFitContent)
                {
                    if (Environment.TickCount - _mouseDownTick < 300)
                    {
                        FitImageSize();
                    }
                }

                _mouseDownTick = Environment.TickCount;
            }

        }
        private void ImageCanvas_MouseUp(object sender, MouseButtonEventArgs e)
        {

            _isMouseDown = false;
        }

        private void ImageCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            var point = e.GetPosition(ImageCanvas);
            if (CanMoveContent == false) return;
            if (e.LeftButton == MouseButtonState.Pressed && _isMouseDown)
            {
                var p = e.GetPosition(RootCanvas);
                var offX = p.X - _pointBuff.X;
                var offY = p.Y - _pointBuff.Y;
                var m = new Matrix(1, 0, 0, 1, offX, offY);
                ImageCanvasMatrix.Matrix = ImageCanvasMatrix.Matrix * m;
                _pointBuff = p;
            }
            else
            {
                _isMouseDown = false;
            }
        }

        private void ImageCanvas_MouseWheel(object sender, MouseWheelEventArgs e)
        {

            if (CanZoomContent == false) return;

            var p = e.MouseDevice.GetPosition(ImageCanvas);

            var m = ImageCanvasMatrix.Matrix;


            if (e.Delta < 0)
            {
                m.ScaleAtPrepend(1 / 1.1, 1 / 1.1, p.X, p.Y);
            }
            else
            {
                m.ScaleAtPrepend(1.1, 1.1, p.X, p.Y);
            }
            ImageCanvasMatrix.Matrix = m;

        }

        public void FillWindow()
        {
            var width = RootCanvas.ActualWidth;
            var height = RootCanvas.ActualHeight;
            if (width != double.NaN && height != double.NaN)
            {
                if (width >= 1 && height >= 1)
                {

                }
            }
        }
        public (bool, string) UpdateWImage(byte[] data, PixelFormat format, int width, int height)
        {
            try
            {
                bool isNew = false;
                if (WImage.Format != format)
                {
                    isNew = true;
                }
                if (width != WImage.PixelWidth || height != WImage.PixelHeight)
                {
                    isNew = true;
                }
                if (_imageRect.HasArea == false)
                {
                    _imageRect = new Int32Rect(0, 0, width, height);
                }
                if (_imageRect.Width != width || _imageRect.Height != height)
                {
                    _imageRect = new Int32Rect(0, 0, width, height);
                }
                if (isNew)
                {
                    WImage = new WriteableBitmap(width, height, 96, 96, format, null);
                }

                int factor = 1;
                if (format == PixelFormats.Bgr24)
                {
                    factor = 3;
                }
                WImage.WritePixels(_imageRect, data, width * factor, 0);
                CtrlHeight = WImage.PixelHeight;
                CtrlWidth = WImage.PixelWidth;
                if (isNew)
                {
                    FitImageSize();
                }
                RaisePropertyChanged(nameof(WImage));
                return (true, null);
            }
            catch (Exception ex)
            {
                return (false, ex.Message);
            }
        }
        public void FitImageSize()
        {

            var width = RootCanvas.ActualWidth;
            var height = RootCanvas.ActualHeight;
            double radio = 1;

            if (WImage.PixelHeight != 0)
            {
                radio = height / WImage.PixelHeight;
            }

            var m = ImageCanvasMatrix.Matrix;
            m.Invert();
            ImageCanvasMatrix.Matrix = m * ImageCanvasMatrix.Matrix;

            m = new Matrix();
            m.Scale(radio, radio);
            ImageCanvasMatrix.Matrix = m * ImageCanvasMatrix.Matrix;

            double widthTmp = CtrlWidth * radio;
            m = new Matrix(1, 0, 0, 1, (width - widthTmp) / 4, 1);
            ImageCanvasMatrix.Matrix = m * ImageCanvasMatrix.Matrix;

        }
        public (bool, string) UpdateWImage(IntPtr data, int bufferSize, PixelFormat format, int width, int height)
        {
            try
            {
                if (_imageDataBuffer == null)
                {
                    _imageDataBuffer = new byte[bufferSize];
                }
                else
                {
                    if (_imageDataBuffer.Length != bufferSize)
                    {
                        _imageDataBuffer = new byte[bufferSize];
                    }
                }
                Marshal.Copy(data, _imageDataBuffer, 0, bufferSize);
            }
            catch (Exception ex)
            {
                return (false, ex.Message);
            }
            return UpdateWImage(_imageDataBuffer, format, width, height);
        }
        public WriteableBitmap WImage { get; set; }
        public double CtrlWidth { get => _ctrlWidth; set { if (_ctrlWidth == value) return; _ctrlWidth = value; RaisePropertyChanged(nameof(CtrlWidth)); } }
        public double CtrlHeight { get => _ctrlHeight; set { if (_ctrlHeight == value) return; _ctrlHeight = value; RaisePropertyChanged(nameof(CtrlHeight)); } }

        #region
        internal static readonly DependencyProperty CanDoubleClickToFitContentProperty =
           DependencyProperty.Register("CanDoubleClickToFitContent", typeof(bool), typeof(UsrImageView), new FrameworkPropertyMetadata()
           {
               BindsTwoWayByDefault = true,
               DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
               DefaultValue = true
           });
        internal static readonly DependencyProperty CanMoveContentProperty =
           DependencyProperty.Register("CanMoveContent", typeof(bool), typeof(UsrImageView), new FrameworkPropertyMetadata()
           {
               BindsTwoWayByDefault = true,
               DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
               DefaultValue = true
           });
        internal static readonly DependencyProperty CanZoomContentProperty =
           DependencyProperty.Register("CanZoomContent", typeof(bool), typeof(UsrImageView), new FrameworkPropertyMetadata()
           {
               BindsTwoWayByDefault = true,
               DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
               DefaultValue = true
           });
        public bool CanDoubleClickToFitContent
        {
            get => (bool)GetValue(CanDoubleClickToFitContentProperty);
            set => SetValue(CanDoubleClickToFitContentProperty, value);
        }
        public bool CanMoveContent
        {
            get => (bool)GetValue(CanMoveContentProperty);
            set => SetValue(CanMoveContentProperty, value);
        }
        public bool CanZoomContent
        {
            get => (bool)GetValue(CanZoomContentProperty);
            set => SetValue(CanZoomContentProperty, value);
        }
        //CanDoubleClickToFitContent
        //CanMoveContent
        //CanZoomContent
        #endregion

        #region PropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(storage, value))
            {
                return false;
            }
            storage = value;
            RaisePropertyChanged(propertyName);
            return true;
        }

        protected virtual bool SetProperty<T>(ref T storage, T value, Action onChanged, [CallerMemberName] string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(storage, value))
            {
                return false;
            }
            storage = value;
            onChanged?.Invoke();
            RaisePropertyChanged(propertyName);
            return true;
        }

        protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
        {
            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }

        protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            this.PropertyChanged?.Invoke(this, args);
        }
        #endregion PropertyChanged
    }
}
